use std::{collections::HashMap, str::FromStr};
use crate::http::{error::HttpParseError, utils, FetchFromBytes, FromBytes, HttpContentType};

#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct HttpRequestHeaders {
    pub host: Option<String>,
    pub user_agent: Option<String>,
    pub accept: Option<String>,
    pub content_type: Option<HttpContentType>,
    pub content_length: Option<u64>,
    pub connection: Option<String>,
    pub upgrade: Option<String>,
    pub sec_websocket_version: Option<String>,
    pub sec_websocket_key: Option<String>,
    pub authorization: Option<String>,
    pub cookie: Option<String>,
    pub other: HashMap<String, String>,
}

impl HttpRequestHeaders {
    pub fn set_keep_alive(&mut self, keep_alive: bool) {
        self.connection = Some(if keep_alive { "keep-alive".into() } else { "close".into() });
    }
    
    pub fn is_keep_alive(&self) -> bool {
        !self.connection.as_ref()
            .map(|v| v.eq_ignore_ascii_case("close"))
            .unwrap_or(false)
    }
}

impl FromStr for HttpRequestHeaders {
    type Err = HttpParseError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let mut headers = Self::default();

        for line in s.lines() {
            if let Some((key, value)) = line.split_once(":") {
                let key = key.trim();
                let value = value.trim();

                match key.to_ascii_lowercase().as_str() {
                    "host" => headers.host = Some(value.to_string()),
                    "user-agent" => headers.user_agent = Some(value.to_string()),
                    "accept" => headers.accept = Some(value.to_string()),
                    "content-type" => headers.content_type = Some(HttpContentType::from_str(value)?),
                    "content-length" => headers.content_length = value.parse().ok(),
                    "connection" => headers.connection = Some(value.to_string()),
                    "upgrade" => headers.upgrade = Some(value.to_string()),
                    "sec-websocket-version" => headers.sec_websocket_version = Some(value.to_string()),
                    "sec-websocket-key" => headers.sec_websocket_key = Some(value.to_string()),
                    "authorization" => headers.authorization = Some(value.to_string()),
                    "cookie" => headers.cookie = Some(value.to_string()),
                    _ => {
                        headers.other.insert(key.to_string(), value.to_string());
                    }
                }
            }
        }

        Ok(headers)
    }
}

impl FetchFromBytes for HttpRequestHeaders {
    type Err = HttpParseError;
    fn fetch_from_bytes(bytes: &[u8]) -> Result<(&[u8], Self), Self::Err> {
        match utils::find_bytes(&bytes, b"\r\n\r\n") {
            Some(line_end) => {
                let line: HttpRequestHeaders = FromBytes::from_bytes(&bytes[..line_end])?;
                Ok((&bytes[line_end+4..], line))
            }
            None => Err(HttpParseError::InvalidRequest("No '\\r\\n\\r\\n' found in request headers".into())),
        }
    }
}

impl ToString for HttpRequestHeaders {
    fn to_string(&self) -> String {
        let mut result = String::new();

        if let Some(v) = &self.host {
            result.push_str(&format!("Host: {}\r\n", v));
        }
        if let Some(v) = &self.user_agent {
            result.push_str(&format!("User-Agent: {}\r\n", v));
        }
        if let Some(v) = &self.accept {
            result.push_str(&format!("Accept: {}\r\n", v));
        }
        if let Some(v) = &self.content_type {
            result.push_str(&format!("Content-Type: {}\r\n", v.to_string()));
        }
        if let Some(v) = &self.content_length {
            result.push_str(&format!("Content-Length: {}\r\n", v));
        }
        if let Some(v) = &self.connection {
            result.push_str(&format!("Connection: {}\r\n", v));
        }
        if let Some(v) = &self.upgrade {
            result.push_str(&format!("Upgrade: {}\r\n", v));
        }
        if let Some(v) = &self.sec_websocket_key {
            result.push_str(&format!("Sec-websocket-Key: {}\r\n", v));
        }
        if let Some(v) = &self.sec_websocket_version {
            result.push_str(&format!("Sec-Websocket-Version: {}\r\n", v));
        }
        if let Some(v) = &self.authorization {
            result.push_str(&format!("Authorization: {}\r\n", v));
        }
        if let Some(v) = &self.cookie {
            result.push_str(&format!("Cookie: {}\r\n", v));
        }

        for (k, v) in &self.other {
            result.push_str(&format!("{}: {}\r\n", k, v));
        }

        result.push_str("\r\n");
        result
    }
}